package com.cfp4cloud.cfp.knowledge.service.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.cfp4cloud.cfp.common.core.util.R;
import com.cfp4cloud.cfp.knowledge.dto.AiFlowExecuteDTO;
import com.cfp4cloud.cfp.knowledge.entity.AiFlowEntity;
import com.cfp4cloud.cfp.knowledge.mapper.AiFlowMapper;
import com.cfp4cloud.cfp.knowledge.service.AiFlowService;
import com.cfp4cloud.cfp.knowledge.support.flow.core.FlowException;
import com.cfp4cloud.cfp.knowledge.support.flow.model.AiFlowDSLDefinition;
import com.cfp4cloud.cfp.knowledge.support.flow.model.processor.AiFlowProcessor;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;

import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Objects;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicReference;

/**
 * AI流程服务实现类
 * <p>
 * 管理和执行AI工作流 支持流式和非流式执行模式 提供流程复制和导出功能
 *
 * @author cfp
 * @date 2025-03-03 10:21:32
 */
@Service
@RequiredArgsConstructor
public class AiFlowServiceImpl extends ServiceImpl<AiFlowMapper, AiFlowEntity> implements AiFlowService {

	/**
	 * 线程池执行器 用于异步执行流程任务
	 */
	private final ExecutorService executorService = Executors.newCachedThreadPool();

	/**
	 * AI流程处理器 执行具体的流程逻辑
	 */
	private final AiFlowProcessor flowProcessor;

	/**
	 * 流程数据访问层
	 */
	private final AiFlowMapper aiFlowMapper;

	/**
	 * 执行工作流
	 * <p>
	 * 支持流式和非流式两种执行模式 流式模式使用SSE推送执行过程数据 非流式模式直接返回执行结果
	 * @param executeDTO 工作流执行参数
	 * @return 流式返回SseEmitter，非流式返回执行结果
	 * @throws FlowException 当工作流不存在、DSL解析失败或执行出错时抛出
	 */
	@Override
	public Object executeFlow(AiFlowExecuteDTO executeDTO) {
		// 查询工作流配置
		AiFlowEntity flow = aiFlowMapper.selectById(executeDTO.getId());
		if (flow == null) {
			throw FlowException.invalidFlow("未找到指定工作流");
		}

		String dslValue = flow.getDsl();
		if (StrUtil.isBlank(dslValue)) {
			throw FlowException.invalidDSL("工作流DSL不能为空");
		}

		AiFlowDSLDefinition dsl = JSONUtil.toBean(dslValue, AiFlowDSLDefinition.class);
		if (dsl == null) {
			throw FlowException.invalidDSL("工作流DSL解析失败");
		}

		try {
			// 根据执行模式处理流程
			if (executeDTO.isStream()) {
				SseEmitter emitter = new SseEmitter(300000L);
				AtomicReference<Boolean> isCompletion = new AtomicReference<>(false);
				emitter.onCompletion(() -> isCompletion.set(true));

				executeDTO.setCallback(data -> {
					try {
						if (!isCompletion.get() && Objects.nonNull(data.getData())
								&& StrUtil.isNotBlank(data.getData().getContent())) {
							emitter.send(SseEmitter.event().data(data));
						}
					}
					catch (IOException e) {
						log.error("SSE send error", e);
						emitter.completeWithError(e);
					}
				});

				Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
				executorService.execute(() -> {
					try {
						SecurityContextHolder.getContext().setAuthentication(authentication);
						flowProcessor.execute(flow, dsl, executeDTO);
					}
					finally {
						emitter.complete();
					}
				});
				return emitter;
			}

			return R.ok(flowProcessor.execute(flow, dsl, executeDTO));
		}
		catch (Exception e) {
			log.error("执行工作流失败", e);
			throw FlowException.executeError(e.getMessage());
		}
	}

	/**
	 * 复制流程
	 * <p>
	 * 创建流程的副本，自动添加Copy后缀 新流程将获得新的ID
	 * @param id 原流程ID
	 * @return 复制成功返回成功响应
	 */
	@Override
	public R copyFlow(Long id) {
		AiFlowEntity aiFlowEntity = baseMapper.selectById(id);
		aiFlowEntity.setId(null);
		aiFlowEntity.setName(aiFlowEntity.getName() + "Copy");
		baseMapper.insert(aiFlowEntity);
		return R.ok();
	}

	/**
	 * 导出流程定义
	 * <p>
	 * 将流程配置导出为DSL文件 导出的文件可用于流程迁移或备份
	 * @param id 流程ID
	 * @param response HTTP响应对象
	 */
	@SneakyThrows
	@Override
	public void exportFlow(Long id, HttpServletResponse response) {
		AiFlowEntity aiFlowEntity = baseMapper.selectById(id);
		aiFlowEntity.setId(null);
		String jsonPrettyStr = JSONUtil.toJsonPrettyStr(aiFlowEntity);

		// 将JSON转换为字节数组
		byte[] data = jsonPrettyStr.getBytes(StandardCharsets.UTF_8);

		response.reset();
		response.setHeader(HttpHeaders.CONTENT_DISPOSITION,
				String.format("attachment; filename=%s.dsl", aiFlowEntity.getName()));
		response.addHeader(HttpHeaders.CONTENT_LENGTH, String.valueOf(data.length));
		response.setContentType("application/octet-stream; charset=UTF-8");
		IoUtil.write(response.getOutputStream(), false, data);
	}

}
